#include <thread>
#include "Main.h"

//include the following external .cpp files to re-enable SpriteBatch:
//SpriteBatch.cpp, VertexTypes.cpp and CommonStates.cpp

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void handleInput(void);
void renderScene(void);

Config* config;
Graphics* graphics;
Controls* controls;
Audio* audio;
PrecisionTimer* timer;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	config = new Config();

	HWND hWnd;
	WNDCLASSEX wc;
	ZeroMemory(&wc, sizeof(WNDCLASSEX));
	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc   = WindowProc;
	wc.hInstance     = hInstance;
	wc.hCursor	     = LoadCursor(NULL, IDC_ARROW);
	wc.lpszClassName = "WindowClass";
	RegisterClassEx(&wc);

    RECT wr = {0, 0, Config::SCREENRES_HOR, Config::SCREENRES_VER};
	AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);

	hWnd = CreateWindowEx(NULL,
						  "WindowClass",
						  "DirectX Graphics",
						  WS_OVERLAPPEDWINDOW,
						  0,
						  0,
						  wr.right - wr.left,
						  wr.bottom - wr.top,
						  NULL,
						  NULL,
						  hInstance,
						  NULL);
	ShowWindow(hWnd, nCmdShow);

    timer = new PrecisionTimer();
	graphics = new Graphics(hWnd, config);
	graphics->PrepareFrameRender();
    controls = new Controls(config, graphics->GetCamera(), graphics);
	audio = new Audio(hWnd, config);
	
	//Execute this command to capture the mouse in the window automatically:
	//SetCapture(hWnd);

	std::thread(renderScene).detach();

	MSG msg;
	while (true) {
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);

			if (msg.message == WM_QUIT) {
				break;
			} else if (msg.message == WM_KEYDOWN) {
				controls->keyDown(msg.wParam);
			} else if (msg.message == WM_KEYUP) {
				controls->keyUp(msg.wParam);
			} else if (msg.message == WM_MOUSEMOVE) {
				//when this event occurs, WPARAM is a combination of words that identify pressed modifier keys (CTRL, Shift, etc).
				//LPARAM is a 16-bit word. The low-order word is the X-coordinate of the cursor
				//the high-order word is the Y position of the cursor.
				//source: http://msdn.microsoft.com/en-us/library/windows/desktop/ms645613(v=vs.85).aspx
				controls->MouseMovement(msg.wParam, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
			}
		}
	}
	graphics->CleanD3D();
	return msg.wParam;
}

void renderScene() {
	double secondsSinceLastFrame = 0;
	double deltaInSecs = 0.0;

	while (true){

		timer->Tick();
		//returns current performance counter frequency. This cannot change while the system is running.
		//http://msdn.microsoft.com/en-us/library/windows/desktop/ms644905%28v=vs.85%29.aspx
		__int64 countsPerSec; //should be about 2.5 billion because I use a 2.53 GHz CPU. Not always consistent.
		QueryPerformanceCounter((LARGE_INTEGER*)&countsPerSec);
		
		double secondsPerCount = 1.0 / (double)countsPerSec;
		deltaInSecs = timer->DeltaTime();// * secondsPerCount;
		double fps = 1.0 / deltaInSecs;

		if (config->FrameCapped) {
			if (secondsSinceLastFrame < 1.0/config->Target_FPS) {
				secondsSinceLastFrame += deltaInSecs;
			} else {
				controls->Move(config->Target_FPS);
				graphics->RenderFrame(deltaInSecs, config->Target_FPS);
				secondsSinceLastFrame = 0;
			}
		} else {
			controls->Move(fps);
			graphics->RenderFrame(deltaInSecs, fps);
		}
	}
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch(message) {
        case WM_DESTROY: {
            PostQuitMessage(0);
            return 0;
        } break;
    }
    return DefWindowProc (hWnd, message, wParam, lParam);
}